package org.mentalsmash.crossroads;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;

/**
 * The Crossword Class represent a crossword schema. It defines a matrix of characters (known as the crossword matrix) and defines across and down
 * entries inside this matrix. A definition is associated to each entry, and this class provides methods to set solutions for each entry.
 * Note that this class does not provide nothing to solve the crossword, but just defines it. Use a CrossowrdSolver to solve an istance of this class.
 * 
 * NOTE: Setting a solution for an entry which crosses oher entries may change characters in the solution of other entries. No check is done in this sense.
 * @author Andrea Reale
 *
 */
public class Crossword {

	
	private static Logger log = Logger.getLogger(Crossword.class);
	/** Character representing a black cell in the crossword matrix. Current implementation should be '~' (Don't count on that)*/
	public static final char BLACK_CELL_CHAR = '~';
	/** Character representing a blank cell in the crossword matrix. Current implementation should be '' (Don't count on that)*/
	public static final char BLANK_CELL_CHAR = ' ';
	/* internal representation for a crossword, organized by rows and columns*/
	private char[][] _crosswordMatrix;
	private int _numRows;
	private int _numCols;
	private String _lang;
	private String _crosswordId;
	
	//definitions entries for the crossword. They are ashed on a string representing the entry id
	private HashMap<String,CrosswordEntry> _entries;

	/**
	 * Number of rows in the crossword
	 * @return the number of row of this crossword instance
	 */
	public int getNumRows()
	{
		return _numRows;
	}
	
	/**
	 * 
	 * @return the number of columns of this crossword instance
	 */
	public int getNumCols(){
		return _numCols;
	}
	
	/**
	 * Creates an instance of Crossowrd, given the parameters described below
	 * @param numRows the number of rows for the crossword matrix
	 * @param numCols the number of columns for the crossword matrix
	 * @param holesMap a set of points describing the way holes (i.e. black cells) are positioned in the crossword. Each point (row,column) univocally locates a black in the crossword.
	 * @param acrDefinitions a List containing across defintion. The order (which is RELEVANT) is the "natural" order in which one will find definitions for a paper printed crossword.
	 * @param dwnDefinitions a List containing down defintions. The order (which is RELEVANT) is the "natural" order in which one will find definitions for a paper printed crossword.
	 * @throws CrosswordException if something goes wrong during instantiation
	 */
	public Crossword(String id,int numRows, int numCols, Set<Point> holesSet, 
			List<String> acrDefinitions,List<String> dwnDefinitions,String lang) throws CrosswordException {
		
		this(id,numRows,numCols,holesSet);
		setLang(lang);
		setAllDefinitions(acrDefinitions, dwnDefinitions);

	}
	
	/**
	 * Creates an instance of Crossowrd, given the parameters described below (all definitions will be empty)
	 * @param numRows the number of rows for the crossword matrix
	 * @param numCols the number of columns for the crossword matrix
	 * @param holesMap a set of points describing the way holes (i.e. black cells) are positioned in the crossword. Each point (row,column) univocally locates a black in the crossword.
	 * @throws CrosswordException if something goes wrong during instantiation
	 */
	public Crossword(String id,int numRows, int numCols, Set<Point> holesSet) throws CrosswordException {

		setCrosswordId(id);
		if(!areHolesConsistent(numCols,numRows,holesSet))
			throw new CrosswordException("Holes schema is not consistent with crossword dimension");


		_numRows = numRows;
		_numCols = numCols;
		
		//crossword schema initialization
		log.debug("Initializing crossword schema...");
		initCrossword(holesSet, 10,10);
		setFriendlyNumbers();
		log.debug("Done.");
	}
	

	/**
	 * Does the crossword initialization (hard) work
	 * @param holesMap a map describing the way holes (i.e. black cells) are positioned in the crossword. Each point (row,column) univocally locates a black in the crossword.
	 * @param acrDefinitions a Map containing across defintion. The keys of the map are the starting cell (expressed as a point (row,column)) of a word.
	 * @param dwnDefinitions a Map containing down defintions. The keys of the map are the starting cell (expressed as a point (row,column)) of a word.
	 * @throws CrosswordException if something goes wrong during instantiation
	 */
	private void initCrossword(Set<Point> holesMap,int acrEntriesNum, int dwnEntriesNum) throws CrosswordException {
		_crosswordMatrix = new char[_numRows][_numCols];
		//setting holes in matrix
		setHoles(_crosswordMatrix,holesMap);

		
		_entries = new HashMap<String,CrosswordEntry>(acrEntriesNum + dwnEntriesNum);
		
		//for each column remembers where the last hole was found
		int[] lastDownHole = new int[_numCols];
		for(int i=0; i < _numCols; i++) lastDownHole[i] = -1;
		//Since I'm scanning by rows I need just an int for remembering the last hole in each row
		int lastAcrossHole;
		
		
		int acrossWordsCtr = 0;
		int downWordsCtr = 0;
		
		
		//scanning the matrix by rows and by columns
		// - When an hole is found
		//    -Check if an across or a down word with length > 1 can be formed
		//    	- Across word will start from the current row and column lastAcrossHole+1, and will end in the current row and column (currentCol -1).
		//      - A down word will start from the row lastDownHole[currentCol] +1 and column currentCol and will end in the row (currentRow -1) and column currentCol.
		// - When the end of a row is found, and it is not an hole cell
		//    - Check if a word can be formed starting from  column lastAcrossHole+1 for the current row and ending at the last column for the row
		// - when a cell of the last row is scanned and it is not an hole cell
		//    - Check if a word can be formed starting from row lastDownHole[currCol]+1 and ending in the currentPos
		for (int currentRow = 0 ; currentRow < _numRows ; currentRow++) {
			lastAcrossHole = -1;
			for (int currentCol = 0; currentCol < _numCols; currentCol++) {
				
				switch (_crosswordMatrix[currentRow][currentCol]) {
				
				case BLACK_CELL_CHAR:
					//checking if an across word can be formed
					int acrWordLength = currentCol -1 - lastAcrossHole;
					if(acrWordLength > 1){
						acrossWordsCtr++;
						Point startPoint = new Point(currentRow,lastAcrossHole+1);
						//String definition = acrDefinitions.get(startPoint);
//						if (definition == null){
//							log.warn("Undefined definition for entry " + startPoint + " across" );
//							definition = "";
//						}
						CrosswordEntry tmp = new CrosswordEntry(buildId(startPoint,true), acrWordLength,"",/*definition*/true,this,
								startPoint);
						_entries.put(tmp.getId(), tmp);
					}

					//now checking if a down word can be formed
					int dwnWordLength = currentRow -1 - lastDownHole[currentCol];
					if(dwnWordLength > 1){
						downWordsCtr++;
						Point startPoint = new Point(lastDownHole[currentCol]+1,currentCol);
						
//						String definition = dwnDefinitions.get(startPoint);
//						if (definition == null){
//							log.warn("Undefined definition for entry " + startPoint + " down" );
//							definition = "";
//						}
						
						CrosswordEntry tmp = new CrosswordEntry(buildId(startPoint, false),dwnWordLength,"",/*definition,*/false,this,
								startPoint);
						_entries.put(tmp.getId(), tmp);
					}

					//updating last found holes
					lastAcrossHole = currentCol;
					lastDownHole[currentCol] = currentRow;
					break;
				
				default:
					//initializing to blank all non hole cells
					_crosswordMatrix[currentRow][currentCol] = BLANK_CELL_CHAR;
					//do nothing unless we are in the last row or in the last cell of a row
					if(currentCol == _numCols -1 ){
						//check whether an across word can be formed
						acrWordLength = currentCol - lastAcrossHole;
						if(acrWordLength > 1){
							acrossWordsCtr++;
							Point startPoint = new Point(currentRow,lastAcrossHole+1);
//							String definition = acrDefinitions.get(startPoint);
//							if (definition == null){
//								log.warn("Undefined definition for entry " + startPoint + " across" );
//								definition = "";
//							}
							CrosswordEntry tmp = new CrosswordEntry(buildId(startPoint, true), acrWordLength,""/*definition,*/,true,
									this,startPoint);
							_entries.put(tmp.getId(), tmp);
						}

					}
					if(currentRow == _numRows -1){
						//check whether a down word can be formed
						dwnWordLength = currentRow - lastDownHole[currentCol];
						if(dwnWordLength > 1){
							downWordsCtr++;
							Point startPoint = new Point(lastDownHole[currentCol]+1,currentCol);
//							String definition = dwnDefinitions.get(startPoint);
//							if (definition == null){
//								log.warn("Undefined definition for entry " + startPoint + " down" );
//								definition = "";
//							}
							CrosswordEntry tmp = new CrosswordEntry(buildId(startPoint, false),dwnWordLength,"",/*definition,*/false,this,
									startPoint);
							_entries.put(tmp.getId(), tmp);
						}
					}
					break;


				}
			}
		}

	}
	
	public String getLang() {
		return _lang;
	}

	private void setLang(String lang) {
		if(!Pattern.matches("\\w\\w", lang))
			throw new IllegalArgumentException("Language must be a two characters string");
		_lang = lang;
	}

	private void setAllDefinitions(List<String>across, List<String> down) throws CrosswordException{
		List<CrosswordEntry> aEntries = getOrderedAcrossEntries();
		List<CrosswordEntry> dEntries = getOrderedDownEntries();
		if(across.size() != aEntries.size() || down.size() != dEntries.size())
			throw new CrosswordException("Definitions list size and number of entries do not match");
		
		int i = 0;
		for(CrosswordEntry aEntry : aEntries)
			aEntry.setDefinition(across.get(i++));
		i=0;
		for(CrosswordEntry dEntry : dEntries)
			dEntry.setDefinition(down.get(i++));
			
		
	}

	/**
	 * 
	 * @param id the id of the desired entry
	 * @return the CrosswordEntry describing the entry or null if no such entry exists
	 */
	protected CrosswordEntry getEntry(String id){
		if(id == null)
			throw new IllegalArgumentException("Entry id must not be null");
		
		return _entries.get(id);
	}
	/**
	 * @return an ordered collection of the across entries for this crossword
	 */
	protected List<CrosswordEntry> getOrderedAcrossEntries(){
		ArrayList<CrosswordEntry> unsorted = new ArrayList<CrosswordEntry>();
		
		for(String entryId : _entries.keySet())
		{
			if( Pattern.matches("\\d{1,2}_\\d{1,2}_a", entryId))
				unsorted.add(_entries.get(entryId));
		}
		
		
		
		Collections.sort(unsorted, _entriesComparator);
		return unsorted;
	}
	
	/**
	 * 
	 * @return an ordered collection of the down entries for this crossword
	 */
	protected List<CrosswordEntry> getOrderedDownEntries(){
		ArrayList<CrosswordEntry> unsorted = new ArrayList<CrosswordEntry>();
		
		for(String entryId : _entries.keySet())
		{
			if( Pattern.matches("\\d{1,2}_\\d{1,2}_d", entryId))
				unsorted.add(_entries.get(entryId));
		}
		
		
		
		Collections.sort(unsorted, _entriesComparator);
		return unsorted;
	}

	/**
	 * Gets the char in the crossword cell located in the point (row,col)
	 * @param row the row of the cell
	 * @param col the column of the cell
	 * @return the character currently in the cell, or Crossword.BLACK_CELL_CHAR if it is a Black cell, or Crossword.BLANK_CELL_CHAR if the cell is blank
	 */
	public char getCharAt(int row, int col)
	{
		if(row <0 || row >= getNumRows() || col <0 || col >= getNumCols() )
			throw new RuntimeException("Index for crossword out of bounds");
		
		return _crosswordMatrix[row][col];
	}
	
	/**
	 * Set a single character in a given position
	 * @param row the row of the cell
	 * @param col the column of the cell
	 * @param val the value to five to the char
	 */
	private void setCharAt(int row,int col,char val)
	{
		if(val == Crossword.BLACK_CELL_CHAR)
			throw new IllegalArgumentException("Can't set an hole");
		if(row <0 || row >= getNumRows() || col <0 || col >= getNumCols() )
			throw new IllegalArgumentException("Index for crossword out of bounds");
		
		_crosswordMatrix[row][col] = val;
	}
	
	/**
	 * Given a set of holes set the holes in the map
	 * @param crosswordMatrix the matrix to set the holes int
	 * @param holesMap a set of java.awt.Point describes where the holes are in the map
	 */
	private void setHoles(char[][] crosswordMatrix, Set<Point> holesMap) {
		for(Point h : holesMap) {
			crosswordMatrix[h.x][h.y] = BLACK_CELL_CHAR;
		}
		
	}

	/**
	 * Checks wheter the holes in a holes set are consistent with the dimensions of the crossword matrix
	 * @param numHorizCells the num of columns of the matrix
	 * @param numVertCells the num of rows of the matrix
	 * @param holesmap the map of holes to check consistency of
	 * @return true if the holes in the map are consistent with the crossword matrix
	 */
	private boolean areHolesConsistent(int numHorizCells, int numVertCells,
			Set<Point> holesmap) {
		for(Point hole : holesmap){
			if (hole.x < 0 || hole.x >= numVertCells )
				return false;
			if(hole.y < 0 || hole.y >= numHorizCells )
				return false;
		}
		return true;
	}
	

	/**
	 * Gets the definitions for a crossword entry
	 * @param id the identifier of the entry in the crossword
	 * @return the definition for the word solution of the entry
	 * @throws CrosswordException it the provided id is not valid
	 */
	public String getDefinition(String id) throws CrosswordException
	{
		CrosswordEntry entry = _entries.get(id);
		if(entry == null)
			throw new CrosswordException("Referred entry with unknown id: " + id);
		return entry.getDefinition();
	}
	
	/**
	 * Set the definition for the entry responding to the given id
	 * @param id the identifier of the entry to set the defintion for
	 * @param definition the definition to set
	 * @throws CrosswordException if the passed definition string is null, or if the id is invalid.
	 */
	public void setDefintion(String id,String definition) throws CrosswordException
	{
		CrosswordEntry entry = _entries.get(id);
		if(entry == null)
			throw new CrosswordException("Referred entry with unknown id: " + id);
		
		if(definition == null){
			log.warn("someone tryed to set a null definition");
			throw new CrosswordException("Cannot set a null defintions");
		}
		entry.setDefinition(definition);
		
	}
	
	/**
	 * Returns a soultion (if set) for a crossword entry
	 * @param id the identifier of the entry whithin the crowssword
	 * @return a possible soultion for the entry
	 * @throws UnsolvedException it the entry is still unsolved
	 * @throws CrosswordException if something else goes wrong (e.g. invalid id provided)
	 */
	public String getSolution(String id) throws UnsolvedException, CrosswordException
	{
		CrosswordEntry entry = _entries.get(id);
		if(entry == null)
			throw new CrosswordException("Referred entry with unknown id: " + id);
		return entry.getSolution();
	}
	
//	/**
//	 * You should really review this method
//	 * @param eng
//	 */
//	public void solve(SolvingEngine eng){
//		eng.solve(this);
//	}
	
	/**
	 * Sets the solution word for an entry with a given id.
	 * @param id the id of the entry to set the solution of
	 * @param sol the solution to set (it should have the same length of getEntryLength(id)
	 * @throws CrosswordException if the soultion is not suitable for the entry
	 */
	public void setSolution(String id, String sol) throws CrosswordException
	{
		CrosswordEntry entry = _entries.get(id);
		if(entry == null)
			throw new CrosswordException("Referred entry with unknown id: " + id);
		entry.setSolution(sol);
	}
	
	/**
	 * Returns the length of the solution of an entry given its id
	 * @param id the identifier of the entry
	 * @return the length of the solution word for the entry
	 * @throws CrosswordException if id is not a valid id
	 */
	public int getEntryLength(String id) throws CrosswordException
	{
		CrosswordEntry entry = _entries.get(id);
		if(entry == null)
			throw new CrosswordException("Referred entry with unknown id: " + id);
		return entry.length();
	}
	
	/**
	 * Gets an array of points containing (in order) the points of the crossword matrix occupied by the characters of
	 * the solution of the entry with the given id
	 * 
	 * @param id the id of the entry. (see documentation for the class Crossword to see how id works)
	 * @return an array of points containing (in order) the points of the crossword matrix occupied by the characters of the solution of this entry
	 * @throws CrosswordException if the id is not valid
	 */
	public Point[] getEntryCells(String id) throws CrosswordException{

		CrosswordEntry entry = _entries.get(id);
		if(entry == null)
			throw new CrosswordException("Referred entry with unknown id: " + id);
		return entry.getCells();

	}
	
	@Override
	public String toString() {
		String toRet = "";
		for(int i=0; i < _numRows; i++){
			for (int j=0; j < _numCols; j++) {
				toRet += _crosswordMatrix[i][j] + " ";
			}
			toRet += "\n";
		}
		
		return toRet;
	}
	
	/**
	 * Utility method that builds the id of an entry in the correct format
	 * @param startPoint the starting point of the entry for wich the id is to build
	 * @param isAcross true if the entry is across. false if it is a down entry
	 * @return a string which is the correct id for the entry described by the input parameters
	 */
	private String buildId(Point startPoint, boolean isAcross){
		return startPoint.x + "_" + startPoint.y + "_" + (isAcross ? "a" : "d");
	}
	
	
	
	public void setCrosswordId(String crosswordId) {
		_crosswordId = crosswordId;
	}

	public String getCrosswordId() {
		return _crosswordId;
	}



	/**
	 * Orders Crossword entry following traditional crossword entries ordering rules (note that the ordering for down entries is something weird)
	 */
	private static Comparator<CrosswordEntry> _entriesComparator = new Comparator<CrosswordEntry>(){

		@Override
		public int compare(CrosswordEntry arg0, CrosswordEntry arg1) {
			if(arg0 == null || arg1 == null){
				log.warn("comparing null entries while ordering across entries");
				throw new IllegalArgumentException("Cannot compare null entries");
			}
			//if the first entry is located in a row higher then the second's row, the firs entry is considered "smaller"
			if(arg0.getStartPoint().x < arg1.getStartPoint().x)
				return -1;
			else if(arg0.getStartPoint().x == arg1.getStartPoint().x) {
				if (arg0.getStartPoint().y < arg1.getStartPoint().y)
					return -1;
				else if (arg0.getStartPoint().y > arg1.getStartPoint().y)
					return 1;
				else return 0;
			}
			else return 1;
		}
		
	};
	
	
	/**
	 * Describes an entry (i.e. a word slot (wich may be across or down) ) in the crossword
	 * @author Andrea Reale
	 *
	 */
	class CrosswordEntry {
		//identifies the position of the entry inside the whole crossword schema:
		// it is a 2 or 3 characters id:
		// the first part is an integer between 1 and 99.
		// the second is an 'a' or a 'd'.
		// The letter discriminates whether the it is an across or a down entry, while the first number orders all
		// the entries in the schema
		//
		private String _id;
		private boolean _across;
		

		private Point _startPoint;
		private int _wordLength;
		private String _definition;
		private String  _solution;
		private int _friendlyNumber;
		private Crossword _cwd;
		
		
		
		
		
		public boolean isAcross() {
			return _across;
		}
		
		/**	
		 * identifies the position of the entry inside the whole crossword schema:
		 * it is a 2 or 3 characters id:
		 * the first part is an integer between 1 and 99.
		 * the second is an 'a' or a 'd'.
		 * The letter discriminates whether the it is an across or a down entry, while the first number orders all
		 * the entries in the schema
		 * 
		 * @return entry-s id
		 */
		public String getId(){
			return _id.toString();
		}
		
		/**
		 * 
		 * @return the length of the solution of this entry
		 */
		public int length(){
			return _wordLength;
		}
		
		/**	
		 * set the id of an entry
		 * the id identifies the position of the entry inside the whole crossword schema:
		 * it is described by the regex \d{1,2}_\d{1,2}_[ad] 
		 * the first two numbers identifies the start position of the entry within the matric
		 * the a or d letter discriminates between across and down definitions
		 * 
		 * @param id a well formed id
		 * @throws CrosswordException if the id is not well formed
		 */
		 private void setId(String id) throws CrosswordException{
			//Checking wheter the id is in the right format
			//TODO: Note that thi id actually accepts 0 as a valid id
			if (!Pattern.matches("\\d{1,2}_\\d{1,2}_[ad]", id))
				throw new CrosswordException("Invalid id for entriy. The id was: " + id);
			else{
				_id = id;
			}
			
			
		}
		 
		 /**
		  * 
		  * @return the definition associated to this entry
		  */
		public String getDefinition() {
			return _definition;
		}

		/**
		 * Change the definition associated to this entry
		 * @param definition the new definition to set
		 */
		private void setDefinition(String definition) {
			_definition = definition;
		}

		/**
		 *
		 * @return The word that solves this entry
		 * @throws UnsolvedException if the entry is still unsolved
		 */
		public String getSolution() throws UnsolvedException {
			if (_solution == null)
				throw new UnsolvedException("Crossword entry is still unsolved");
			else
				return _solution;
		}

		/**
		 * set a new solution for this entry
		 * @param solution the soultion for this entry, it should be a word of the same length of the return value of the length() method  called on this entry
		 * @throws CrosswordException if provided solution's length is wrong
		 */
		private void setSolution(String solution) throws CrosswordException{
			//Checking consistency between solution and length of the entry
			if(solution.length() != _wordLength)
				throw new CrosswordException("The provided solution's length is incompatible with entry's length");
			else {
				_solution = solution;
				
				//setting solution in crossword internal representation
				if(_across)
					setAcrossSolution(solution);
				else
					setDownSolution(solution);
				
			}
		}

		public int getFriendlyNumber() {
			return _friendlyNumber;
		}

		private void setFriendlyNumber(int friendlyNumber) {
			_friendlyNumber = friendlyNumber;
		}

		/**
		 * Commodity method to set a solution for a down entry
		 * @param solution the solution to set
		 */
		private void setDownSolution(String solution) {
			int col = _startPoint.y;
			for (int i = _startPoint.x,j=0; i< (_startPoint.x + _wordLength) ; i++,j++)
			{
				_cwd.setCharAt(i, col, solution.charAt(j));
			}
			
		}

		/**
		 * Commodity method to set a solution for an across entry
		 * @param solution the solution to set
		 */
		private void setAcrossSolution(String solution) {
			int row = _startPoint.x;
			for (int i = _startPoint.y,j=0; i< (_startPoint.y + _wordLength) ; i++,j++)
			{
				_cwd.setCharAt(row, i, solution.charAt(j));
			}
			
		}
		
		public Point[] getCells(){
			Point[] cells = new Point[this.length()];
			
			Point startPoint = getStartPoint();
			if (isAcross()) {
				
				//from the starting column to the last column
				for(int i = 0; i<this.length();i++){
					Point p = new Point(startPoint.x,startPoint.y + i);
					cells[i] = p;
				}
				//points are already sorted by ascending column
				
			} else {
				//from the starting row to the last column
				for(int i = 0; i<this.length();i++){
					Point p = new Point(startPoint.x +i,startPoint.y);
					cells[i] = p;
				}
			}
			
			return cells;
		}

		/**
		 * Sets whether this entry is an across or a down entry
		 * @param isAcross true if this entry is across. false if it is a down entry
		 * @throws CrosswordException if the provided value and the id of this entry do not match
		 */
		private void setAcross( boolean isAcross) throws CrosswordException{
			boolean isIdAcross = Pattern.matches("^.+a", getId());
			if ( (isIdAcross && !isAcross) || (!isAcross && isAcross))
				throw new CrosswordException("Id and across switch doesn't match");
			else _across = isAcross;	
		}
		
		/**
		 * Sets (and performs (hopefully) appropriates checks) the start point of this entry in the crossword matrix 
		 * @param startPoint the point in the crossword matrix where this entry starts
		 * @throws CrosswordException if the provided starts point is a black cell, or is outside the crossword matrix
		 */
		private void setStartPoint(Point startPoint) throws CrosswordException
		{
			if(startPoint.x < 0 || startPoint.x >= _cwd._numRows || startPoint.y <0 || startPoint.y >= _cwd._numCols )
				throw new CrosswordException("Start point for entry exeeds crossword dimensions");
			if(getCharAt(startPoint.x, startPoint.y) == BLACK_CELL_CHAR)
				throw new CrosswordException("Start point can't be in a Hole Cell");
			
			_startPoint = startPoint;
		}
		
		/**
		 * Gets an array of points containing (in order) the points of the crossword matrix occupied by the characters of
		 * the solution of this entry
		 * 
		 * @return an array of points containing (in order) the points of the crossword matrix occupied by the characters of the solution of this entry
		 */
		public Point getStartPoint(){
			return _startPoint;
		}
		
		/**
		 * Instantiates a new crossword entry
		 * @param id the id for the new entry. An entry id is like \d{1_2}_\d{1_2}_[ad]. the first two numbers (from 0 to 99) represents the coordinats where the entry starts \
		 * the final letter (a or d) tells whether this entry is an across or a down entry
		 * @param wordLength the length of the word which is the solution for this entry
		 * @param definition the defintions of the word which is the solution for this entry
		 * @param isAcross true if it is an across definition. false if it is down. Note that this value must agree with the desired id.
		 * @param cwd the instance of crossword this entry refers to
		 * @param startPoint the point where the entry starts. This point should agree whith the desired id, but NOTE that NO CHECK IS CURRENTLY PERFORMED!!
		 * @throws CrosswordException if something goes wrong during instantioation
		 */
		public CrosswordEntry(String id, int wordLength, String definition, boolean isAcross, 
				Crossword cwd, Point startPoint) throws CrosswordException {
			
			setId(id);
			setAcross(isAcross);
			
			
			_definition = definition;
			_wordLength = wordLength;
			_cwd = cwd;
		    setStartPoint(startPoint);
		    _friendlyNumber=-1;
		}
	}
	
	
	private void setFriendlyNumbers(){
		int counter = 1;
		
		for(int curRow =0 ; curRow<getNumRows() ; curRow++){
			for(int curCol = 0; curCol<getNumCols(); curCol++ ){
				CrosswordEntry current = null;
				boolean assigned = false;
				//If there is an across entry in this position
				if((current=_entries.get(curRow + "_" + curCol + "_a"))!=null){
					current.setFriendlyNumber(counter);
					assigned = true;
				}
				//If there is a down entry in this position
				if((current=_entries.get(curRow + "_" + curCol + "_d"))!=null){
					current.setFriendlyNumber(counter);
					assigned = true;
				}
				
				if(assigned) counter++;
			}
		}
	}
	
	
	
		
		
		

}






